#!/usr/bin/python
from __future__ import print_function

## Import list
import dbase
import utils
import s3
import argparse
import string
import sys
import gURL
from types import *
from os.path import expanduser, getsize, abspath, exists, dirname, isdir, basename
from os import getcwd, makedirs, remove, walk, stat, link, symlink, unlink, system
from shutil import copy2
import logging
from config import ConfigManager
import pdriveStrings

## Assume debug logging level
loggingLevel = logging.DEBUG

## Set up inital logger
logFormat = '%(asctime)s %(levelname)s module:%(module)s func:%(funcName)s line:%(lineno)s %(message)s'
logging.basicConfig(format=logFormat, level=loggingLevel)
logging.debug("Startup: {} {}".format(__file__, __name__))

# Get the current working directory
cwd = abspath(getcwd())
logging.debug("cwd is '{}'".format(cwd))

## Setup arg parser
parser = argparse.ArgumentParser(description = pdriveStrings.description, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("-c", "--config", nargs = 1, default = "config/pdrive.conf", help="Path to config file", type=str)
parser.add_argument("--hash", help="hash", type=str)
parser.add_argument("--id", help="id; used sometimes as an alternate identified", type=str)
parser.add_argument("--nl", help="adds a newline char where one may be left off", action="store_true")
parser.add_argument("--showrate", help="shows rating", action="store_true")
parser.add_argument("--showdescr", help="shows description", action="store_true")
parser.add_argument("--configkey", help="config key to get/set", type=str)
parser.add_argument("--search", help="string for which to search", type=str)
parser.add_argument("--seconds", help="integer of seconds (default: 300)", type=int, default=300)
parser.add_argument("--localdir", help="local directory", type=str)
parser.add_argument("--tag", help="tag", type=str, action="append")
parser.add_argument("--mediatype", help="media type", type=str)
parser.add_argument("--filetype", help="file type", type=str)
parser.add_argument("--rate", help="rating, 1 -5", choices="12345", type=str)
parser.add_argument("--descr", help="media descrition string", type=str)
parser.add_argument("--s3", help="s3", action="store_true")
parser.add_argument("--quiet", help="quiet", action="store_true")
parser.add_argument("--onedir", help="exports files into a single directory", action="store_true")
parser.add_argument("--port", help="port to which to bind", type=int, default=8080)

## Link/copy selectors
groupFileExports = parser.add_mutually_exclusive_group()
groupFileExports.add_argument("--hard", help="creates hard links, as opposed to soft/symlink", action="store_true")
groupFileExports.add_argument("--soft", help="creates hard links, as opposed to soft/symlink", action="store_true")
groupFileExports.add_argument("--copy", help="creates hard links, as opposed to soft/symlink", action="store_true")

## Config key selectors
groupSetKeys = parser.add_mutually_exclusive_group()
groupSetKeys.add_argument("--configvalue", help="config value to set", type=str)
groupSetKeys.add_argument("--settrue", help="set value to bool true", action="store_true")
groupSetKeys.add_argument("--setfalse", help="set value to bool false", action="store_true")

## Expect one or more commands
parser.add_argument("commands", nargs="+")

## Parse the args
try:
	args = parser.parse_args()
except:
	print("Try '--help'")
	raise


## Extract super and sub commands
commands = args.commands
try:
    superCommand = commands[0]
except:
	logging.critical("Missing super command")
	usage()
	raise SystemExit

try:
    subCommand = string.lower(commands[1])
except:
    subCommand = None

logging.debug("Got commands: super ({}) sub ({})".format(superCommand, subCommand))

## Init is a special case...
## Run it, then die
if superCommand == "init":
	from cli.init import init
	init(cwd)
	raise SystemExit

# Fetch and set up the config file args, if set
if type(args.config) == StringType:
	configFile = args.config
if type(args.config) == ListType:
	configFile = args.config[0] 

## Load up config file
if configFile.startswith('~'):
	configFile = expanduser(configFile)

if not exists(configFile):
	for root, dirs, files in walk("{}/config".format(cwd)):
		configFile = "{}/{}".format(root, files[0])

## New config instance
config = ConfigManager(configFile)

## Load up the config values
sqliteDbaseFile = config.sqliteDbaseFile
importPath = config.importPath
logFile = config.logFile
logLevel = config.logLevel
projectName = config.projectName
lighttpdSocket = config.lighttpdSocket

if sqliteDbaseFile is None:
	logging.critical("dbase is not set in config file!")
	raise SystemExit
if importPath is None:
	logging.info("importpath is not set in config file")
if logFile is None:
	logging.info("logFile is not set in config file; logging here will continue")
if logLevel is None:
	logging.info("logLevel is not set in config file; logging will continue at this level")
if projectName is None:
	logging.critical("projectname is not set in config file!")
	raise SystemExit
if lighttpdSocket is None:
	logging.warn("lighttpdSocket is not set in config; web server may not function correctly")


if logFile is None:
	logging.debug("Switching over to config'ed logging: filename ({}) log level ({})".format(logFile, logLevel))
	if logging.root:
		del logging.root.handlers[:]
	## Determine config file specified logging
	if logLevel == "info":
		logLevel = logging.INFO
	elif logLevel == "warn":
		logLevel = logging.WARNING
	elif logLevel == "error":
		logLevel = logging.ERROR
	elif logLevel == "crit":
		logLevel = logging.CRITICAL
	elif logLevel == "error":
		logLevel = logging.ERROR
	elif logLevel == "debug":
		logLevel = logging.DEBUG
	else:
		logLevel = logging.INFO
else:
	logging.debug("Config 'logFile' is not set; no change to logging")

	## Setup logging as per config file
	logFormat = '%(asctime)s %(levelname)s module:%(module)s func:%(funcName)s line:%(lineno)s %(message)s'
	logging.basicConfig(format=logFormat, filename=logFile, level=logLevel)

## Super Command 'server' is another special case
if superCommand == "server":
	from httpd import runServer
	try:
		runServer(config.get("lighttpd", "socket"), sqliteDbaseFile, config.get("lighttpd", "secureDownloadKey"))
	except:
		raise
	raise SystemExit
	

## Connect to dbase
if sqliteDbaseFile.startswith('~'):
	sqliteDbaseFile = expanduser(sqliteDbaseFile)

try:
	db = dbase.DBase(sqliteDbaseFile)
	logging.info("Connected to sqlite3 dbase")
except dbase.CantConnect as e:
	logging.critical("Cannot connect to sqlite database file ({}): {}".format(sqliteDbaseFile, e))
	raise SystemExit

## Load up basic config keys
pdriveFiles = db.fetchKeyValue("pdrive_files")
pdriveRoot = db.fetchKeyValue("pdrive_root")

##
## 'media' Super Command
##
if superCommand == "media":
	from cli.media import Media
	media = Media(args, db, config)
	logging.debug("'media' super command")

	## Sub Command: addtag
	if subCommand == "addtag":
		logging.info("running media addtag")
		try:
			media.addTag()
		except media.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))
		except:
			raise

	## Sub Command: checkrepo
	elif subCommand == "checkrepo":
		logging.info("running media checkrepo")
		try:
			media.checkRepo()
		except:
			raise

	## Sub Command: descr
	if subCommand == "descr":
		logging.info("running media descr")
		try:
			media.descr()
		except cli.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))

	## Sub Command: details
	elif subCommand == "details":
		logging.info("running media details")
		try:
			media.details()
		except media.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))

	## Sub Command: export
	elif subCommand == "export":
		logging.info("running media details")
		try:
			media.export()
		except media.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))
		
	## Sub Command: import	
	elif subCommand == "import":
		logging.info("running media import")
		try:
			media.mport()
		except media.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))
		except media.CLIExceptionPathNotExist as e:
			print("Import path ({}) does not exist".format(e))
		except media.CLIExceptionNotADir as e:
			print("Import path ({}) is not a directory".format(e))
		except media.CLIExceptionUserCancel as e:
			print("User canceled proccess")
		except:
			raise

	## Sub Command: ls
	elif subCommand == "ls":
		logging.info("running media ls")
		try:
			media.ls();
		except:
			raise
	
	## Sub Command: lsfiletypes
	elif subCommand == "lsfiletypes":
		logging.info("running media lsfiletypes")
		try:
			media.lsFileTypes()
		except:
			raise

	## Sub Command: lsmediatypes
	elif subCommand == "lsmediatypes":
		logging.info("running media lsmediatypes")
		try:
			media.lsMediaTypes()
		except:
			pass
	
	## Sub Command: pathto
	elif subCommand == "pathto":
		logging.info("running media pathto")
		try:
			media.pathTo()
		except media.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))
		except:
			raise

	## Sub Command: rate
	elif subCommand == "rate":
		logging.info("running media rate")
		try:
			media.rate()
		except media.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))
		except:
			raise

	## Sub Command: rm
	elif subCommand == "rm":
		logging.info("running media rm")
		try:
			media.rm()
		except media.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))
		except:
			raise

	## Sub Command: rmtag
	elif subCommand == "rmtag":
		logging.info("running media rmtag")
		try:
			media.rmTag()
		except media.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))
		except:
			raise

	## Sub Command: mkhtmk
	elif subCommand == "mkhtml":
		logging.info("running media mkhtml")
		try:
			media.mkHtml()
		except media.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))
		except:
			raise

	## Bad or missing sub command
	else:
		print ("Bad or missing sub command for super command 'media'")
		parser.print_help()
		raise SystemExit

##
## 'tag' Super Command
##
elif superCommand == "tag":
	from cli.tag import Tag
	tag = Tag(args, db, config)
	logging.debug("'tag' super command")

	## Sub Command: add 
	if subCommand == "add":
		logging.debug("running tag add")
		try:
			tag.add()
		except tag.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))
		except:
			raise

	## Sub Command: ls
	elif subCommand == "ls":
		logging.debug("running tag ls")
		try:
			tag.ls()
		except:
			raise

	## Sub Command: rm 
	elif subCommand == "rm":
		logging.debug("running tag rm")
		try:
			tag.rm()
		except tag.CLIExceptionMissingArg as e:
			print("Missing arg --{}".format(e))
		except:
			raise

	## Bad or missing sub command
	else:
		print ("Bad or missing sub command for super command 'tag'")
		parser.print_help()
		raise SystemExit

##
## 's3' Super Command
##
elif superCommand == "s3":
	logging.debug("'s3' super command")

	## Special Case... S3 won't init
	## without basic info... so, set it
	## up first.
	if subCommand == "setup":
		logging.debug("running s3 sync")
		try:
			db.setKeyValue("aws_id", raw_input("AWS ID ([enter] for none/skip): "))
			db.setKeyValue("aws_key", raw_input("AWS Key ([enter] for none/skip): "))
			db.setKeyValue("aws_s3_bucket", raw_input("AWS S3 Bucket ([enter] for none/skip): "))
			db.setKeyValue("aws_s3_objectPrefix", raw_input("AWS S3 Object Prefix; such as 'myPDrive/' ([enter] for none/skip): "))
			db.setKeyValue("aws_s3_encrypt", raw_input("S3 Server Side Encyption ([enter] for none/skip, 1 = yes, 0 = no): "))
			db.setKeyValue("aws_s3_reducedRedun", raw_input("S3 Storage Reduced Redundancy ([enter] for none/skip, 1 = yes, 0 = no): "))
		except:
			raise
		raise SystemExit
	
	## For the rest of the sub commands,
	## load up the s3 classes
	from cli.s3 import S3CLI
	import s3

	try:
		s3CLI = S3CLI (
			args, 
			db,
			config,
			s3.S3(
				db.fetchKeyValue("aws_id"), 
				db.fetchKeyValue("aws_key"), 
				db.fetchKeyValue("aws_s3_bucket"), 
				db.fetchKeyValue("aws_s3_reducedRedun"), 
				db.fetchKeyValue("aws_s3_encrypt")
			)
		)
	except s3.S3.ExceptionMissingS3Param as e: 
		logging.critical("Can't init S3 connection; missing required param: {}". format(e))
		raise SystemExit


	if subCommand == "sync":
		s3CLI.sync()
				
	elif subCommand == "geturl":
		if not args.hash:
			print("s3 geturl requires --hash")
			raise SystemExit
		if not db.hashInS3(args.hash):
			print("Hash {} is not sync'ed to s3".format(args.hash))
			raise SystemExit
		media = db.mediaInfo(args.hash)
                sha256Hash = media["sha256Hash"]
                filesize = media["filesize"]
                filetype = media["fileType"]
                hmac = utils.hmacSHA256(str(filesize), sha256Hash)
		keyname = "{}{}.{}".format(objectPrefix, hmac, filetype)
		parts = gURL.shorten(s3.getURL(keyname, args.seconds))
		if parts["status"] == True:
			print(parts["shortURL"])
		else:
			print("Unable to shorten URL")
			print(parts)
	else:
		print ("Bad or missing sub command for super command 's3'")
		parser.print_help()
		raise SystemExit
			
##
## 'image' Super Command
##
elif superCommand == "image":
	from cli.image import Image
	i = Image(args, db, config)
	logging.debug("'image' super command")

	## Sub Command: mkthumbs 
	if subCommand == "mkthumbs":
		logging.debug("running image mkthumbs")
		try:
			i.makeThumbs()
		except:
			raise
	## Sub Command:  
	elif subCommand == "rmthumbs":
		logging.debug("running image rmthumbs")
		try:
			i.rmThumbs()
		except:
			raise
	else:
		print ("Bad or missing sub command for super command 'image'")
		parser.print_help()
		raise SystemExit

##
## 'video' Super Command
##
elif superCommand == "video":
	from cli.video import Video
	v = Video(args, db, config)
	logging.debug("'video' super command")

	## Sub Command: mkthumbs 
	if subCommand == "mkthumbs":
		logging.debug("running video mkthumbs")
		try:
			v.makeThumbs()
		except:
			raise
	## Sub Command:  
	elif subCommand == "rmthumbs":
		logging.debug("running video rmthumbs")
		try:
			v.rmThumbs()
		except:
			raise
	else:
		print ("Bad or missing sub command for super command 'video'")
		parser.print_help()
		raise SystemExit


elif superCommand == "configs":
	if subCommand == "setvalue":
		if not args.configkey:
			print("configs setvalue requires --configkey")
			raise SystemExit
		if args.settrue:
			args.configvalue = True
		elif args.setfalse:
			args.configvalue = False
		elif not args.configvalue:
			print("configs setvalue requires --configvalue (or --settrue/--setfalse)")
			raise SystemExit
		db.setKeyValue(args.configkey, args.configvalue)
		print("Value of {} set to: {}".format(args.configkey, args.configvalue))
	elif subCommand == "getvalue":
		if not args.configkey:
			print("configs setvalue requires --configkey")
			raise SystemExit
		
		value = db.fetchKeyValue(args.configkey)
		print("Value of {}: {} (type: {})".format(args.configkey, value, type(value)))
	else:
		print ("Bad or missing sub command for super command 'dbase'")
		parser.print_help()
		raise SystemExit

elif superCommand == "view":
	mediaList = db.mediaInfo(sha256Hash = args.hash, tagLimitList = args.tag, mediaType =  args.mediatype, search = args.search)
	mediaListCount = len(mediaList)
	if mediaListCount > 10:
		userConfirm = "-"
		while userConfirm[0] not in ['y', 'Y', 'n', 'N']:
			userConfirm = raw_input("You are about to view {} items. Do you wish to continue? (y/n): ".format(mediaListCount))
		if userConfirm[0] in ['n', 'N']:
			raise SystemExit

	videoViewer = db.fetchKeyValue("video_viewer")
	videoViewerArgs = db.fetchKeyValue("video_viewer_args")
	if videoViewerArgs is not None:
		videoViewerArgs = "-{}".format(videoViewerArgs)
	else:
		videoViewerArgs = ""
	imageViewer = db.fetchKeyValue("image_viewer")

	for details in mediaList:
		hmac = utils.hmacSHA256(str(details["filesize"]), details["sha256Hash"])
		hashbyte = hmac[0]
		fqpn = "{}/{}/{}.{}".format(pdriveFiles, hashbyte, hmac, details["fileType"])

		if details["mediaType"] == "video":
			system("{} {} {}".format(videoViewer, videoViewerArgs, fqpn))	
		if details["mediaType"] == "image":
			system("{} {}".format(imageViewer, fqpn))

else:
	print ("Bad or missing super command")
	parser.print_help()
